home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / misc / emu / msh-156.lha / han / hansec.c < prev    next >
C/C++ Source or Header  |  1996-12-22  |  33KB  |  1,323 lines

  1. /*-
  2.  * $Id: hansec.c,v 1.56 1996/12/22 00:22:33 Rhialto Rel $
  3.  * $Log: hansec.c,v $
  4.  * Revision 1.56  1996/12/22  00:22:33  Rhialto
  5.  * Fiddle with caching strategy on sector and file handle level.
  6.  * Add some protection against BPB data being 0 (but it still
  7.  * seems not enough).
  8.  *
  9.  * Revision 1.55  1993/12/30  23:28:00    Rhialto
  10.  * Freeze for MAXON5.
  11.  * Keep two sets of disk geometries: for DD and HD floppies.
  12.  * Move start of partition out of this: this prevents your harddisk from
  13.  * resetting the MSH partition to its start on a disk change.
  14.  * Tell outside programs the track size, through the Mount info.
  15.  * Free the boot block if you don't understand it.
  16.  *
  17.  * Revision 1.54  1993/06/24  05:12:49    Rhialto
  18.  * try heuristics for the cache. DICE 2.07.54R.
  19.  *
  20.  * Revision 1.53  92/10/25  02:27:32  Rhialto
  21.  * No real change.
  22.  *
  23.  * Revision 1.51  92/04/17  15:37:19  Rhialto
  24.  * Freeze for MAXON.
  25.  *
  26.  * Revision 1.46  91/10/06  18:25:31  Rhialto
  27.  *
  28.  * Freeze for MAXON
  29.  *
  30.  * Revision 1.43  91/09/28  01:38:43  Rhialto
  31.  * Changed to newer syslog stuff.
  32.  *
  33.  * Revision 1.42  91/06/13  23:48:16  Rhialto
  34.  * DICE conversion; fix cache bug
  35.  *
  36.  * Revision 1.40  91/03/03  18:36:08  Rhialto
  37.  * Freeze for MAXON
  38.  *
  39.  * Revision 1.35  91/03/03  17:42:19  Rhialto
  40.  * Cache list is now two lists: LRU and sorted by sector.
  41.  *
  42.  * Revision 1.33  91/01/24  00:09:38  Rhialto
  43.  * Constrain behaviour of FindFreeSector.
  44.  *
  45.  * Revision 1.32  90/11/23  23:53:51  Rhialto
  46.  * Prepare for syslog
  47.  *
  48.  * Revision 1.31  90/11/10  02:44:35  Rhialto
  49.  * Patch 3a. Changes location of disk volume date.
  50.  *
  51.  * Revision 1.30  90/06/04  23:17:02  Rhialto
  52.  * Release 1 Patch 3
  53.  *
  54.  * HANSEC.C
  55.  *
  56.  * The code for the messydos file system handler.
  57.  *
  58.  * Sector-level stuff: read, write, cache, unit conversion.
  59.  * Other interactions (via MyDoIO) with messydisk.device.
  60.  *
  61.  * This code is (C) Copyright 1989-1994 by Olaf Seibert. All rights reserved.
  62.  * May not be used or copied without a licence.
  63. -*/
  64.  
  65. #include <string.h>
  66. #include "han.h"
  67. #include "dos.h"
  68.  
  69. #if HDEBUG
  70. #   include "syslog.h"
  71. #else
  72. #   define    debug(x)
  73. #endif
  74.  
  75. Prototype struct MsgPort *DiskReplyPort;
  76. Prototype struct IOExtTD *DiskIOReq;
  77. Prototype struct IOStdReq *DiskChangeReq;
  78. Prototype struct DiskParam DefaultDisk;
  79. Prototype struct DiskParam Disk;
  80. Prototype struct Partition Partition;
  81. Prototype byte *Fat;
  82. Prototype short FatDirty;
  83. Prototype short error;
  84. Prototype long    IDDiskState;
  85. Prototype long    IDDiskType;
  86. Prototype struct timerequest *TimeIOReq;
  87. Prototype int    MaxCache;
  88. Prototype ulong BufMemType;
  89. Prototype char    CacheDirty;
  90. Prototype char    DelayCount;
  91. Prototype short CheckBootBlock;
  92. Prototype word    Get8086Word(byte *Word8086);
  93. Prototype word    OtherEndianWord(long oew);     /* long should become word */
  94. Prototype ulong OtherEndianLong(ulong oel);
  95. #if !defined(OtherEndianMsd)
  96. /*Prototype void  OtherEndianMsd (struct MsDirEntry *msd);*/
  97. #endif
  98. Prototype word    ClusterToSector(word cluster);
  99. Prototype word    ClusterOffsetToSector(word cluster, word offset);
  100. Prototype word    DirClusterToSector(word cluster);
  101. Prototype word    SectorToCluster(word sector);
  102. Prototype word    NextCluster(word cluster);
  103. Prototype word    NextClusteredSector(word sector);
  104. Prototype word    FindFreeSector(word prev);
  105. Prototype struct CacheSec *FindSecByNumber(int number);
  106. Prototype struct CacheSec *FindSecByBuffer(byte *buffer);
  107. Prototype struct CacheSec *NewCacheSector(struct MinNode *pred);
  108. Prototype void    FreeCacheSector(struct CacheSec *sec);
  109. Prototype void    InitCacheList(void);
  110. Prototype void    FreeCacheList(void);
  111. Prototype void    MSUpdate(int immediate);
  112. Prototype void    StartTimer(int);
  113. Prototype byte *ReadSec(int sector);
  114. Prototype byte *EmptySec(int sector);
  115. Prototype void    WriteSec(int sector, byte *data);
  116. Prototype void    MayWriteTrack(struct CacheSec *cache);
  117. Prototype void    FreeSec(byte *buffer);
  118. Prototype void    MarkSecDirty(byte *buffer);
  119. Prototype void    WriteFat(void);
  120. Prototype int    AwaitDFx(void);
  121. Prototype int    ReadBootBlock(void);
  122. Prototype int    IdentifyDisk(char *name, struct DateStamp *date);
  123. Prototype void    TDRemChangeInt(void);
  124. Prototype int    TDAddChangeInt(struct Interrupt *interrupt);
  125. Prototype int    TDChangeNum(void);
  126. Prototype int    TDProtStatus(void);
  127. Prototype int    TDMotorOff(void);
  128. Prototype int    TDClear(void);
  129. Prototype int    TDUpdate(void);
  130. Prototype int    MyDoIO(struct IOStdReq *ioreq);
  131.  
  132. struct MsgPort *DiskReplyPort;
  133. struct IOExtTD *DiskIOReq;
  134. struct IOStdReq *DiskChangeReq;
  135. int        HeadOnTrack;
  136.  
  137. struct DiskParam DefaultDisk = {
  138.     MS_BPS,
  139.     MS_SPC,
  140.     MS_RES,
  141.     MS_NFATS,
  142.     MS_NDIRS,
  143.     MS_NSECTS,
  144.     0,        /* MEDIA */
  145.     MS_SPF,
  146.     MS_SPT,
  147.     MS_NSIDES,
  148.     0,        /* NHID */
  149. };
  150.  
  151. struct DiskParam Disk;
  152. struct Partition Partition;
  153. byte           *Fat;
  154. short        FatDirty;    /* Fat must be written to disk */
  155.  
  156. short        error;        /* To put the error value; for Result2 */
  157. long        IDDiskState;    /* InfoData.id_DiskState */
  158. long        IDDiskType;    /* InfoData.id_DiskType */
  159. struct timerequest *TimeIOReq;    /* For motor-off delay */
  160. struct Cache    CacheList;    /* Sector cache */
  161. int        CurrentCache;    /* How many cached buffers do we have */
  162. int        MaxCache;    /* Maximum amount of cached buffers */
  163. long        CacheBlockSize; /* Size of disk block + overhead */
  164. ulong        BufMemType;
  165. char        CacheDirty;    /* Cache must be written to disk */
  166. char        DelayCount;
  167. short        CheckBootBlock; /* Do we need to check the bootblock? */
  168.  
  169. #define LRU_TO_SEC(lru) ((struct CacheSec *)((char *)lru - \
  170.             OFFSETOF(CacheSec, sec_LRUNode)))
  171. #define NN_TO_SEC(nn)    ((struct CacheSec *) nn)
  172.  
  173. long dos_packet1(struct MsgPort *port, long type, long arg1);
  174.  
  175. #if 0
  176. word
  177. Get8086Word(Word8086)
  178. byte  *Word8086;
  179. {
  180.     return Word8086[0] | Word8086[1] << 8;
  181. }
  182.  
  183. word
  184. OtherEndianWord(oew)
  185. word        oew;
  186. {
  187.      return  (oew << 8) |
  188.         ((oew >> 8) & 0xff);
  189. }
  190.  
  191. ulong
  192. OtherEndianLong(oel)
  193. ulong        oel;
  194. {
  195.      return ((oel &      0xff) << 24) |
  196.         ((oel &    0xff00) <<  8) |
  197.         ((oel &   0xff0000) >>  8) |
  198.         ((oel & 0xff000000) >> 24);
  199.  
  200. }
  201. #endif
  202.  
  203. #if !defined(OtherEndianMsd)
  204. void
  205. OtherEndianMsd(msd)
  206. struct MsDirEntry *msd;
  207. {
  208.     msd->msd_Date = OtherEndianWord(msd->msd_Date);
  209.     msd->msd_Time = OtherEndianWord(msd->msd_Time);
  210.     msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster);
  211.     msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize);
  212. }
  213. #endif
  214.  
  215. word
  216. ClusterToSector(cluster)
  217. word    cluster;
  218. {
  219.     return cluster ? Disk.start + cluster * Disk.spc
  220.     : 0;
  221. }
  222.  
  223. word
  224. ClusterOffsetToSector(cluster, offset)
  225. word    cluster;
  226. word    offset;
  227. {
  228.     return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps
  229.     : 0;
  230. }
  231.  
  232. word
  233. DirClusterToSector(cluster)
  234. word    cluster;
  235. {
  236.     return cluster ? Disk.start + cluster * Disk.spc
  237.     : Disk.rootdir;
  238. }
  239.  
  240. word
  241. SectorToCluster(sector)
  242. word    sector;
  243. {
  244.     return sector ? (sector - Disk.start) / Disk.spc
  245.     : 0;
  246. }
  247.  
  248. /*
  249.  * Get the next cluster in a chain. Sort-of checks for special entries.
  250.  * Claims that the file ends if something strange happens.
  251.  */
  252.  
  253. word
  254. NextCluster(cluster)
  255. word cluster;
  256. {
  257.     word entry;
  258.  
  259.     entry = GetFatEntry(cluster);
  260.     if (entry >= 0xFFF7 || entry == 0 || entry > Disk.maxclst)
  261.     return FAT_EOF;
  262.     else
  263.     return entry;
  264. }
  265.  
  266. word
  267. NextClusteredSector(sector)
  268. word        sector;
  269. {
  270.     word        next = (sector + 1 - Disk.start) % Disk.spc;
  271.  
  272.     if (next == 0) {
  273.     next = NextCluster(SectorToCluster(sector));
  274.     return next != FAT_EOF ? ClusterToSector(next)
  275.         : SEC_EOF;
  276.     } else
  277.     return sector + 1;
  278. }
  279.  
  280. #if ! READONLY
  281.  
  282. /*
  283.  * FindFreeSector is like FindFreeCluster, but communicates in terms of
  284.  * sector numbers instead of cluster numbers. This is only useful for
  285.  * directories, since they count in sector numbers because the root
  286.  * directory cannot be expressed in clusters. If a new sector is allocated,
  287.  * the rest of its cluster is allocated as well, of course. The returned
  288.  * sector is always the first sector of a cluster.
  289.  */
  290.  
  291. word
  292. FindFreeSector(prev)
  293. word        prev;
  294. {
  295.     word freecluster = FindFreeCluster(SectorToCluster(prev));
  296.  
  297.     return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster);
  298. }
  299.  
  300. #endif
  301.  
  302. /*
  303.  * Find a specific sector. The cache list is a Least Recently Used stack:
  304.  * Put it on the head of the cache list. So if it is not used anymore in a
  305.  * long time, it bubbles to the end of the list, getting a higher chance
  306.  * of being trashed for re-use.
  307.  * For convenience we remember the preceeding cached sector, for the
  308.  * common case that we want to insert a new sector.
  309.  */
  310.  
  311. struct MinNode *PredNumberNode;
  312.  
  313. struct CacheSec *
  314. FindSecByNumber(number)
  315. int    number;
  316. {
  317.     struct CacheSec *sec;
  318.     struct MinNode  *nextsec;
  319.  
  320.     debug(("FindSecByNumber %ld ", (long)number));
  321.  
  322.     /*
  323.      * IF the most recently used sector has a number not higher than
  324.      * the one we want, start looking there instead of at the
  325.      * lowest sector number.
  326.      * Following the LRU chain further is not so effective since it
  327.      * has a decreasing tendency.
  328.      */
  329.     {
  330.     struct CacheSec *lrusec;
  331.  
  332.     if (CurrentCache > 0 &&
  333.         (lrusec = LRU_TO_SEC(CacheList.LRUList.mlh_Head),
  334.         lrusec->sec_Number <= number)) {
  335.         sec = lrusec;
  336.     } else
  337.         sec = NN_TO_SEC(CacheList.NumberList.mlh_Head);
  338.     }
  339.  
  340.     while (nextsec = sec->sec_NumberNode.mln_Succ) {
  341.     if (sec->sec_Number == number) {
  342.         debug((" (%lx) %lx\n", (long)sec->sec_Refcount, sec));
  343.         Remove((struct Node *)&sec->sec_LRUNode);
  344.         AddHead((struct List *)&CacheList.LRUList,
  345.             (struct Node *)&sec->sec_LRUNode);
  346.         return sec;
  347.     }
  348.     debug(("cache %ld %lx; ", (long)sec->sec_Number, sec));
  349.     if (sec->sec_Number > number) {
  350.         /* We need to insert before this one */
  351.         debug(("insert b4 %ld ", (long)sec->sec_Number));
  352.         break;
  353.     }
  354. #ifdef notdef    /* This improvement is at best marginal I think */
  355.     {
  356.         struct CacheSec *lrusec;
  357.  
  358.         if ((lrusec = NextNode(&sec->sec_LRUNode)) &&
  359.         (lrusec = LRU_TO_SEC(lrusec),
  360.         lrusec->sec_Number > sec->sec_Number) &&
  361.         lrusec->sec_Number <= number) {
  362.         sec = lrusec;
  363.         debug(("++LRU++ "));
  364.         } else
  365.         sec = NN_TO_SEC(nextsec);
  366.     }
  367. #else
  368.     sec = NN_TO_SEC(nextsec);
  369. #endif
  370.     }
  371.  
  372.     /*
  373.      * If we ran off the end of the list, or if it was empty,
  374.      * *sec is now the dummy end marker. In all 3 cases we need its
  375.      * predecessor.
  376.      */
  377.  
  378.     PredNumberNode = sec->sec_NumberNode.mln_Pred;
  379.     debug(("sec = %lx, pred = %lx; ", sec, PredNumberNode));
  380.     return NULL;
  381. }
  382.  
  383. struct CacheSec *
  384. FindSecByBuffer(buffer)
  385. byte           *buffer;
  386. {
  387.     return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data[0]));
  388. }
  389.  
  390. /*
  391.  * Get a fresh cache buffer. If we are allowed more cache, we just
  392.  * allocate memory. Otherwise, we try to find a currently unused buffer.
  393.  * We start looking at the end of the list, which is the bottom of the LRU
  394.  * stack. If that fails, allocate more memory anyway. Not that is likely
  395.  * anyway, since we currently lock only one sector at a time.
  396.  *
  397.  * The desired predecessor in the numerically sorted list is given so
  398.  * we don't have to look it up again.
  399.  */
  400.  
  401. struct CacheSec *
  402. NewCacheSector(pred)
  403. struct MinNode *pred;
  404. {
  405.     struct CacheSec *sec;
  406.     struct MinNode  *nextsec;
  407.  
  408.     debug(("NewCacheSector\n"));
  409.  
  410.     if (CurrentCache < MaxCache) {
  411.     if (sec = AllocMem(CacheBlockSize, BufMemType)) {
  412.         goto add;
  413.     }
  414.     }
  415.     for (sec = LRU_TO_SEC(CacheList.LRUList.mlh_TailPred);
  416.      nextsec = sec->sec_LRUNode.mln_Pred;
  417.      sec = LRU_TO_SEC(nextsec)) {
  418.     if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) {
  419.         /*
  420.          * If the predecessor is being (re)moved, it is no longer a
  421.          * stable point to attach the new or recycled sector to so we
  422.          * need to step back on the list.
  423.          */
  424.         if (&sec->sec_NumberNode == pred)
  425.         pred = sec->sec_NumberNode.mln_Pred;
  426.         debug(("NewCacheSector: dump dirty sec %d\n", sec->sec_Number));
  427.         FreeCacheSector(sec);    /* Also writes it to disk */
  428.         continue;
  429.     }
  430.     if (sec->sec_Refcount == 0) {    /* Implies not SEC_DIRTY */
  431.         if (&sec->sec_NumberNode == pred) /* Same comment */
  432.         pred = sec->sec_NumberNode.mln_Pred;
  433.         debug(("NewCacheSector: re-use clean sec %d\n", sec->sec_Number));
  434.         Remove((struct Node *)&sec->sec_LRUNode);
  435.         Remove((struct Node *)&sec->sec_NumberNode);
  436.         goto move;
  437.     }
  438.     }
  439.  
  440.     sec = AllocMem(CacheBlockSize, BufMemType);
  441.  
  442.     if (sec) {
  443. add:
  444.     CurrentCache++;
  445. move:
  446.     AddHead((struct List *) &CacheList.LRUList,
  447.         (struct Node *) &sec->sec_LRUNode);
  448.     Insert((struct List *) &CacheList.NumberList,
  449.            (struct Node *) &sec->sec_NumberNode,
  450.            (struct Node *) pred);
  451.     } else
  452.     error = ERROR_NO_FREE_STORE;
  453.  
  454.     debug(("NewCacheSector: %lx\n", sec));
  455.     return sec;
  456. }
  457.  
  458. /*
  459.  * Dispose a cached sector, even if it has a non-zero refcount. If it is
  460.  * dirty, write it out.
  461.  */
  462.  
  463. void
  464. FreeCacheSector(sec)
  465. struct CacheSec *sec;
  466. {
  467.     debug(("FreeCacheSector %ld\n", (long)sec->sec_Number));
  468.  
  469.     if (sec->sec_Refcount & ~SEC_DIRTY) {
  470.     debug(("\n\t*** PANIC!!! Refcount not 0 !!! (%x) ***\n\n", sec->sec_Refcount));
  471.     sec->sec_Refcount &= SEC_DIRTY;
  472.     }
  473.  
  474. #if ! READONLY
  475.     if (sec->sec_Refcount & SEC_DIRTY) {
  476.     /*WriteSec(sec->sec_Number, sec->sec_Data);*/
  477.     MayWriteTrack(sec);
  478.     }
  479. #endif
  480.     Remove((struct Node *)&sec->sec_LRUNode);
  481.     Remove((struct Node *)&sec->sec_NumberNode);
  482.     FreeMem(sec, CacheBlockSize);
  483.     CurrentCache--;
  484. }
  485.  
  486. /*
  487.  * Create an empty cache list
  488.  */
  489.  
  490. void
  491. InitCacheList()
  492. {
  493.     extern struct CacheSec *sec;    /* Of course this does not exist... */
  494.  
  495.     NewList((struct List *)&CacheList.LRUList);
  496.     NewList((struct List *)&CacheList.NumberList);
  497.     CurrentCache = 0;
  498.     CacheDirty = 0;
  499.     CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data);
  500. }
  501.  
  502. /*
  503.  * Dispose all cached sectors, possibly writing them to disk.
  504.  */
  505.  
  506. void
  507. FreeCacheList()
  508. {
  509.     struct CacheSec *sec;
  510.  
  511.     debug(("FreeCacheList, %ld\n", (long)CurrentCache));
  512.     while (sec = GetHead(&CacheList.NumberList)) {
  513.     FreeCacheSector(NN_TO_SEC(sec));
  514.     }
  515.     MSUpdate(1);
  516. }
  517.  
  518. /*
  519.  * Eventually write all dirty cache buffers to disk. When we decide to
  520.  * do this, we start writing at the nearest end of the range we need
  521.  * to go through.
  522.  */
  523.  
  524. void
  525. MSUpdate(immediate)
  526. int        immediate;
  527. {
  528.     int         writingfat;
  529.     int         delaycount;
  530.  
  531.     debug(("MSUpdate, imm=%d, count=%d\n", immediate, DelayCount));
  532.  
  533.     if (immediate)
  534.     DelayCount = 1;
  535.  
  536.     if (DelayCount == 0)
  537.     return;
  538.  
  539.     delaycount = DelayCount;
  540. #if ! READONLY
  541.     writingfat = delaycount <= 2 && FatDirty;
  542.  
  543.     /* A special case... sigh... */
  544.     if (delaycount <= 3) {
  545.     WriteDirtyFileLock(RootLock);
  546.     }
  547.  
  548.     if (delaycount <= 3 && CacheDirty) {
  549.     struct CacheSec *sec;
  550.     struct MinNode *nextsec;
  551.     int        lowtrack, hightrack;
  552.     int        offset;
  553.  
  554.     if (CurrentCache > 0) {
  555.         if (writingfat) {
  556.         lowtrack = 0;
  557.         } else {
  558.         sec = NN_TO_SEC(CacheList.NumberList.mlh_Head);
  559.         lowtrack = sec->sec_Number / Disk.spt;
  560.         }
  561.         sec = NN_TO_SEC(CacheList.NumberList.mlh_TailPred);
  562.         hightrack = sec->sec_Number / Disk.spt;
  563.  
  564.         if ((HeadOnTrack - lowtrack) <= (hightrack - HeadOnTrack)) {
  565.         /* closer to low track */
  566.         debug(("MSUpdate: Closer to lower track %d <- %d %d\n",
  567.                lowtrack, HeadOnTrack, hightrack));
  568.         sec = NN_TO_SEC(CacheList.NumberList.mlh_Head);
  569.         offset = OFFSETOF(CacheSec, sec_NumberNode.mln_Succ);
  570.         if (writingfat) {
  571.             WriteFat();
  572.             writingfat = 0;
  573.         }
  574.         } else {
  575.         /* closer to high track */
  576.         debug(("MSUpdate: Closer to higher track %d %d -> %d\n",
  577.                lowtrack, HeadOnTrack, hightrack));
  578.         sec = NN_TO_SEC(CacheList.NumberList.mlh_TailPred);
  579.         offset = OFFSETOF(CacheSec, sec_NumberNode.mln_Pred);
  580.         }
  581.  
  582.         for ( ; nextsec = *(struct MinNode **)((char *)sec + offset);
  583.          sec = NN_TO_SEC(nextsec)) {
  584.         if (sec->sec_Refcount & SEC_DIRTY) {
  585.             WriteSec(sec->sec_Number, sec->sec_Data);
  586.             sec->sec_Refcount &= ~SEC_DIRTY;
  587.         }
  588.         }
  589.     }
  590.     CacheDirty = 0;
  591.     }
  592.     if (writingfat)
  593.     WriteFat();
  594. #endif
  595.  
  596.     if (delaycount <= 1) {
  597. #if ! READONLY
  598.     debug(("MSUpdate, do TDUpdate\n"));
  599.     while (TDUpdate() != 0 && RetryRwError(DiskIOReq))
  600.         ;
  601. #endif
  602.     TDMotorOff();
  603.     }
  604.     if (--DelayCount)
  605.     StartTimer(0);
  606. }
  607.  
  608. /*
  609.  * Start the timer which triggers cache writing and stopping the disk
  610.  * motor.
  611.  */
  612.  
  613. void
  614. StartTimer(times)
  615. int        times;
  616. {
  617.     if (DelayCount < times)
  618.     DelayCount = times;
  619.  
  620.     if (CheckIO((struct IORequest *)TimeIOReq)) {
  621.     WaitIO((struct IORequest *)TimeIOReq);
  622.     TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
  623.     TimeIOReq->tr_time.tv_secs = 2;
  624.     TimeIOReq->tr_time.tv_micro = 0;
  625.     SendIO((struct IORequest *)TimeIOReq);
  626.     debug(("StartTimer(%d) sent TimeIOReq\n", times));
  627.     }
  628. }
  629.  
  630. /*
  631.  * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We
  632.  * allocate a buffer and copy the data in, and lock the buffer until
  633.  * FreeSec() is called.
  634.  */
  635.  
  636. byte           *
  637. ReadSec(sector)
  638. int        sector;
  639. {
  640.     struct CacheSec *sec;
  641.  
  642. #if HDEBUG
  643.     if (sector == 0) {
  644.     debug(("************ ReadSec(0) ***************\n"));
  645.     }
  646. #endif
  647.  
  648.     if (sec = FindSecByNumber(sector)) {
  649.     sec->sec_Refcount++;
  650.  
  651.     return sec->sec_Data;
  652.     }
  653.     if (sec = NewCacheSector(PredNumberNode)) {
  654.     struct IOExtTD *req;
  655.  
  656.     sec->sec_Number = sector;
  657.     sec->sec_Refcount = 1;
  658.  
  659.     debug(("ReadSec %ld\n", (long)sector));
  660.  
  661.     req = DiskIOReq;
  662.     do {
  663.         req->iotd_Req.io_Command = ETD_READ;
  664.         req->iotd_Req.io_Data = (APTR)sec->sec_Data;
  665.         req->iotd_Req.io_Offset = Partition.offset + (long) sector * Disk.bps;
  666.         req->iotd_Req.io_Length = Disk.bps;
  667.         MyDoIO(&req->iotd_Req);
  668.     } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
  669.  
  670.     StartTimer(2);
  671.     HeadOnTrack = sector / Disk.spt;
  672.  
  673.     if (req->iotd_Req.io_Error == 0) {
  674. #if 1
  675.         MayWriteTrack(sec);
  676. #endif
  677.         return sec->sec_Data;
  678.     }
  679.     error = ERROR_NOT_A_DOS_DISK;
  680.     sec->sec_Refcount = 0;
  681.     FreeCacheSector(sec);
  682.     }
  683.     return NULL;
  684. }
  685.  
  686. #if ! READONLY
  687.  
  688. byte           *
  689. EmptySec(sector)
  690. int        sector;
  691. {
  692.     struct CacheSec *sec;
  693.  
  694. #if HDEBUG
  695.     if (sector == 0) {
  696.     debug(("************ EmptySec(0) ***************\n"));
  697.     }
  698. #endif
  699.     if (sec = FindSecByNumber(sector)) {
  700.     sec->sec_Refcount++;
  701.  
  702.     return sec->sec_Data;
  703.     }
  704.     if (sec = NewCacheSector(PredNumberNode)) {
  705.     sec->sec_Number = sector;
  706.     sec->sec_Refcount = 1;
  707.  
  708.     return sec->sec_Data;
  709.     }
  710.  
  711.     return NULL;
  712. }
  713.  
  714. void
  715. WriteSec(sector, data)
  716. int        sector;
  717. byte           *data;
  718. {
  719.     struct IOExtTD *req;
  720.  
  721.     debug(("WriteSec %ld\n", (long)sector));
  722.  
  723.     req = DiskIOReq;
  724.     do {
  725.     req->iotd_Req.io_Command = ETD_WRITE;
  726.     req->iotd_Req.io_Data = (APTR) data;
  727.     req->iotd_Req.io_Offset = Partition.offset + (long) sector * Disk.bps;
  728.     req->iotd_Req.io_Length = Disk.bps;
  729.     MyDoIO(&req->iotd_Req);
  730.     /* debug(("WriteSec error %ld\n", req->iotd_Req.io_Error)); */
  731.     } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
  732.  
  733.     StartTimer(2);
  734.     HeadOnTrack = sector / Disk.spt;
  735. }
  736.  
  737. /*
  738.  * Write all sectors that are on the same track as this one.
  739.  * This speeds up I/O by taking advantage of track buffering and
  740.  * reduction of seeking.
  741.  * As a general rule we don't write sectors which are dirty but
  742.  * still in use. (Not that this is expected to occur often, though).
  743.  */
  744.  
  745. void
  746. MayWriteTrack(cache)
  747. struct CacheSec *cache;
  748. {
  749.     struct CacheSec *c;
  750.     struct MinNode *cn;
  751.     int         lowsec;
  752.     int         highsec;
  753.  
  754.     if (CacheDirty == 0)
  755.     return;
  756.  
  757.     lowsec = (cache->sec_Number / Disk.spt) * Disk.spt;
  758.     highsec = lowsec + Disk.spt;
  759.  
  760.     debug(("MayWriteTrack sec %d (sec %d-%d)\n", cache->sec_Number, lowsec, highsec-1));
  761.  
  762.     for (c = cache; cn = c->sec_NumberNode.mln_Pred; c = NN_TO_SEC(cn)) {
  763.     if (c->sec_Number < lowsec)
  764.         break;
  765.     cache = c;
  766.     }
  767.  
  768.     for (c = cache; cn = c->sec_NumberNode.mln_Succ; c = NN_TO_SEC(cn)) {
  769.     if (c->sec_Number >= highsec)
  770.         break;
  771.     /* don't write active sectors */
  772.     if (c->sec_Refcount == SEC_DIRTY) {
  773.         WriteSec(c->sec_Number, c->sec_Data);
  774.         c->sec_Refcount &= ~SEC_DIRTY;
  775.     }
  776.     }
  777. }
  778.  
  779. #endif
  780.  
  781. /*
  782.  * Unlock a cached sector. When the usage count drops to zero, which
  783.  * implies it is not dirty, and we are over our cache quota, the sector is
  784.  * freed. Otherwise we keep it for re-use.
  785.  */
  786.  
  787. void
  788. FreeSec(buffer)
  789. byte           *buffer;
  790. {
  791.  
  792.     if (buffer) {
  793.     struct CacheSec *sec;
  794.  
  795.     sec = FindSecByBuffer(buffer);
  796. #if HDEBUG
  797.     if (sec->sec_Number == 0) {
  798.         debug(("************ FreeSec(0) ***************\n"));
  799.     }
  800. #endif
  801.     --sec->sec_Refcount;
  802. #if 1
  803. # if 1
  804.     /* Write out the track, if doing it now is cheap */
  805.     if ((sec->sec_Number / Disk.spt) == HeadOnTrack) {
  806.         MayWriteTrack(sec);
  807.     }
  808. # else
  809.     /* Write out the sector, if doing it now is cheap */
  810.     if ((sec->sec_Refcount == SEC_DIRTY) &&
  811.         (sec->sec_Number / Disk.spt) == HeadOnTrack) {
  812.         WriteSec(sec->sec_Number, sec->sec_Data);
  813.         sec->sec_Refcount &= ~SEC_DIRTY;
  814.     }
  815. # endif
  816. #endif
  817. #ifdef notdef
  818.     if (sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */
  819.         if (CurrentCache > MaxCache) {
  820.         FreeCacheSector(sec);
  821.         }
  822.     }
  823. #else
  824.     /*
  825.      * If we need to dump cache then dump some long-unused sectors.
  826.      */
  827.     while (CurrentCache > MaxCache &&
  828.         (sec = LRU_TO_SEC(GetTail(&CacheList.LRUList))) &&
  829.         (sec->sec_Refcount & ~SEC_DIRTY) == 0) {
  830.         debug(("FreeSec: dump %s sec %d\n",
  831.             (sec->sec_Refcount & SEC_DIRTY)? "dirty" : "clean",
  832.             sec->sec_Number));
  833.         FreeCacheSector(sec);
  834.     }
  835. #endif
  836.     }
  837. }
  838.  
  839. #if ! READONLY
  840.  
  841. void
  842. MarkSecDirty(buffer)
  843. byte           *buffer;
  844. {
  845.     struct CacheSec *sec;
  846.  
  847.     if (buffer) {
  848.     sec = FindSecByBuffer(buffer);
  849.     sec->sec_Refcount |= SEC_DIRTY;
  850.     CacheDirty = 1;
  851.     StartTimer(4);
  852.     }
  853. }
  854.  
  855. /*
  856.  * Write out the FAT. Called from MSUpdate(), so don't call it again from
  857.  * here. Don't use precious cache space for it; you could say it has its
  858.  * own private cache already.
  859.  */
  860.  
  861. void
  862. WriteFat()
  863. {
  864.     int    fat,
  865.             sec;
  866.     int         disksec = Disk.res;      /* First FAT, first sector */
  867.  
  868.     debug(("WriteFat()\n"));
  869.     /* Write all FATs */
  870.     for (fat = 0; fat < Disk.nfats; fat++) {
  871.     for (sec = 0; sec < Disk.spf; sec++) {
  872.         WriteSec(disksec++, Fat + sec * Disk.bps);
  873.     }
  874.     }
  875.     FatDirty = FALSE;
  876. }
  877.  
  878. #endif
  879.  
  880. long
  881. dos_packet1(port, type, arg1)
  882. struct MsgPort *port;
  883. long type, arg1;
  884. {
  885.     struct StandardPacket *sp;
  886.     struct MsgPort *rp;
  887.     long res1;
  888.  
  889.     if ((rp = CreatePort(NULL, 0L)) == NULL)
  890.     return DOS_FALSE;
  891.     if ((sp = AllocMem((long)sizeof(*sp), MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
  892.     DeletePort(rp);
  893.     return DOS_FALSE;
  894.     }
  895.     sp->sp_Msg.mn_Node.ln_Name = (char *)&sp->sp_Pkt;
  896.     sp->sp_Pkt.dp_Link = &sp->sp_Msg;
  897.     sp->sp_Pkt.dp_Port = rp;
  898.     sp->sp_Pkt.dp_Type = type;
  899.     sp->sp_Pkt.dp_Arg1 = arg1;
  900.     PutMsg(port, &sp->sp_Msg);
  901.     WaitPort(rp);
  902.     GetMsg(rp);
  903.     res1 = sp->sp_Pkt.dp_Res1;
  904.     FreeMem(sp, (long)sizeof(*sp));
  905.     DeletePort(rp);
  906.     return res1;
  907. }
  908.  
  909. int
  910. AwaitDFx()
  911. {
  912.     debug(("AwaitDFx\n"));
  913.     if (Interleave & NICE_TO_DFx) {
  914.     static char    dfx[] = "DFx:";
  915.     void           *dfxProc;
  916.     char        xinfodata[sizeof(struct InfoData) + 3];
  917.     struct InfoData *infoData;
  918.     int        triesleft;
  919.  
  920.     dfx[2] = '0' + UnitNr;
  921.     infoData = (struct InfoData *)(((long)&xinfodata[3]) & ~3L);
  922.  
  923.     if ((dfxProc = DeviceProc(dfx)) == NULL)
  924.         return 0;
  925.  
  926.     for (triesleft = 10; triesleft; triesleft--) {
  927.         debug(("AwaitDFx %ld\n", (long)triesleft));
  928.  
  929.         dos_packet1(dfxProc, ACTION_DISK_INFO, (long)CTOB(infoData));
  930.         debug(("AwaitDFx %lx\n", infoData->id_DiskType));
  931.         if (infoData->id_DiskType == ID_NO_DISK_PRESENT) {
  932.         /* DFx has not noticed yet. Wait a bit. */
  933.         WaitIO((struct IORequest *)TimeIOReq);
  934.         TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
  935.         TimeIOReq->tr_time.tv_secs = 0;
  936.         TimeIOReq->tr_time.tv_micro = 750000L;    /* .75 s */
  937.         SendIO((struct IORequest *)TimeIOReq);
  938.         continue;
  939.         }
  940.         if ((infoData->id_DiskType & 0xFFFFFF00) == ID_DOS_DISK) {
  941.         /* DFx: understands it, so it is not for us. */
  942.         return 1;
  943.         }
  944.         /*
  945.          * All (well, most) other values mean that DFx: does not
  946.          * understand it, so we can give it a try.
  947.          */
  948.         break;
  949.     }
  950.     }
  951.     return 0;
  952. }
  953.  
  954. int
  955. ReadBootBlock()
  956. {
  957.     int protstatus;
  958.     short    oldCancel = Cancel;
  959.  
  960.     debug(("ReadBootBlock\n"));
  961.     FreeFat();            /* before disk parameters change */
  962.     TDClear();
  963.  
  964.     if (TDProtStatus() >= 0) {
  965.     byte *bootblock;
  966.  
  967.     if (AwaitDFx())
  968.         goto unreadable_disk;
  969.     if ((protstatus = TDProtStatus()) < 0)
  970.         goto no_disk;
  971.  
  972.     TDChangeNum();
  973.     debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count));
  974.  
  975.     Cancel = 1;
  976.     if (bootblock = ReadSec(0)) {
  977.         word    oldbps;
  978.  
  979.         IDDiskType = *(ULONG *)bootblock;
  980.  
  981.         if ((CheckBootBlock & CHECK_BOOTJMP) &&
  982.                 /* Atari: empty or 68000 JMP */
  983.         bootblock[0] != 0x00 && bootblock[0] != 0x4E &&
  984.                 /* 8086 ml for a jump */
  985.         bootblock[0] != 0xE9 && bootblock[0] != 0xEB) {
  986.  
  987.         FreeSec(bootblock);
  988.         /* IDDiskType = ID_NOT_REALLY_DOS; */
  989.         IDDiskType = *(ULONG *)bootblock;
  990.         debug(("bootblock[0] not good.\n"));
  991.         goto not_dos_disk;
  992.         }
  993.         oldbps = Disk.bps;
  994.         if (CheckBootBlock & CHECK_USE_DEFAULT) {
  995.         Disk = DefaultDisk;
  996.         } else {
  997.         Disk.bps = Get8086Word(bootblock + 0x0b);
  998.         Disk.spc = bootblock[0x0d];
  999.         Disk.res = Get8086Word(bootblock + 0x0e);
  1000.         Disk.nfats = bootblock[0x10];
  1001.         Disk.ndirs = Get8086Word(bootblock + 0x11);
  1002.         Disk.nsects = Get8086Word(bootblock + 0x13);
  1003.         Disk.media = bootblock[0x15];
  1004.         Disk.spf = Get8086Word(bootblock + 0x16);
  1005.         Disk.spt = Get8086Word(bootblock + 0x18);
  1006.         Disk.nsides = Get8086Word(bootblock + 0x1a);
  1007.         Disk.nhid = Get8086Word(bootblock + 0x1c);
  1008.         }
  1009.         FreeSec(bootblock);
  1010.  
  1011.     recalculate:
  1012.         /*
  1013.          *    Maybe the sector size just changed. Who knows?
  1014.          */
  1015.         if (Disk.bps != oldbps) {
  1016.         FreeCacheList();
  1017.         InitCacheList();
  1018.         }
  1019.  
  1020.         /*
  1021.          * Check some things no matter what the user wants,
  1022.          * because they might cause MSH to crash otherwise.
  1023.          */
  1024.         if (Disk.bps == 0 || Disk.spc == 0 || Disk.nsides == 0) {
  1025.         debug(("0 disk parameters.\n"));
  1026.         goto insane_disk;
  1027.         }
  1028.  
  1029.         Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps;
  1030.         Disk.rootdir = Disk.res + Disk.spf * Disk.nfats;
  1031.         Disk.datablock = Disk.rootdir + Disk.ndirsects;
  1032.         Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc;
  1033.         /* Available clusters are 2..maxclust in secs start..nsects-1 */
  1034.         Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1;
  1035.         Disk.bpc = Disk.bps * Disk.spc;
  1036.         Disk.vollabel = FakeRootDirEntry;
  1037. /*        Disk.fat16bits = Disk.nsects > 20740;  / * DOS3.2 magic value */
  1038.         Disk.fat16bits = Disk.maxclst >= 0xFF7; /* DOS3.0 magic value */
  1039.  
  1040.         /*
  1041.          * We set this for the benefit of ouside programs; this value
  1042.          * will not be used directly by us, since it is reset to one
  1043.          * of the defaults on every disk change or format.
  1044.          */
  1045.         if (Environ)
  1046.         Environ->de_BlocksPerTrack = Disk.spt;
  1047.  
  1048.         debug(("%lx\tbytes per sector\n", (long)Disk.bps));
  1049.         debug(("%lx\tsectors per cluster\n", (long)Disk.spc));
  1050.         debug(("%lx\treserved blocks\n", (long)Disk.res));
  1051.         debug(("%lx\tfats\n", (long)Disk.nfats));
  1052.         debug(("%lx\tdirectory entries\n", (long)Disk.ndirs));
  1053.         debug(("%lx\tsectors\n", (long)Disk.nsects));
  1054.         debug(("%lx\tmedia byte\n", (long)Disk.media));
  1055.         debug(("%lx\tsectors per FAT\n", (long)Disk.spf));
  1056.         debug(("%lx\tsectors per track\n", (long)Disk.spt));
  1057.         debug(("%lx\tsides\n", (long)Disk.nsides));
  1058.         debug(("%lx\thidden sectors\n", (long)Disk.nhid));
  1059.  
  1060.         debug(("%lx\tdirectory sectors\n", (long)Disk.ndirsects));
  1061.         debug(("%lx\troot dir block\n", (long)Disk.rootdir));
  1062.         debug(("%lx\tblock for (imaginary) cluster 0\n", (long)Disk.start));
  1063.         debug(("%lx\tfirst data block\n", (long)Disk.datablock));
  1064.         debug(("%lx\tclusters total\n", (long)Disk.maxclst));
  1065.         debug(("%lx\tbytes per cluster\n", (long)Disk.bpc));
  1066.         debug(("%lx\t16-bits FAT?\n", (long)Disk.fat16bits));
  1067.  
  1068.         /*
  1069.          * Sanity check.
  1070.          */
  1071.         if (CheckBootBlock & CHECK_SANITY) {
  1072.         if (Disk.spc < 1 ||
  1073.             Disk.nfats < 1 ||
  1074.             Disk.ndirs < 8 ||
  1075.             Disk.nsects < 640 ||
  1076.             Disk.spf < 1 ||
  1077.             Disk.spt < 1 ||
  1078.             Disk.nsides < 1 ||
  1079.             Disk.ndirsects < 1) {
  1080.         insane_disk:
  1081.             if (CheckBootBlock & CHECK_SAN_DEFAULT) {
  1082.             debug(("Bad bootblock; using default values.\n"));
  1083.             oldbps = Disk.bps;
  1084.             Disk = DefaultDisk;
  1085.             goto recalculate;
  1086.             } else {
  1087.             IDDiskType = ID_NOT_REALLY_DOS;
  1088.             debug(("Bad bootblock; refusing disk.\n"));
  1089.             goto not_dos_disk;
  1090.             }
  1091.         }
  1092.         }
  1093.  
  1094.         IDDiskType = ID_DOS_DISK;
  1095. #if READONLY
  1096.         IDDiskState = ID_WRITE_PROTECTED;
  1097. #else
  1098.         if (protstatus > 0)
  1099.         IDDiskState = ID_WRITE_PROTECTED;
  1100.         else
  1101.         IDDiskState = ID_VALIDATED;
  1102. #endif
  1103.  
  1104.         if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40)
  1105.         DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS;
  1106.         else
  1107.         DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS;
  1108.  
  1109.         GetFat();
  1110.     } else {
  1111.         debug(("Can't read %ld.\n", (long)DiskIOReq->iotd_Req.io_Error));
  1112.     unreadable_disk:
  1113.         IDDiskType = ID_UNREADABLE_DISK;
  1114.     not_dos_disk:
  1115.         FreeCacheList();
  1116.         error = ERROR_NO_DISK;
  1117.         IDDiskState = ID_VALIDATING;
  1118.     }
  1119.     }
  1120. #if HDEBUG
  1121.     else debug(("No disk inserted %ld.\n", (long)DiskIOReq->iotd_Req.io_Error));
  1122. #endif
  1123. no_disk:
  1124.  
  1125.     Cancel = oldCancel;
  1126.     return 1;
  1127. }
  1128.  
  1129. /*
  1130.  * We try to identify the disk currently in the drive, trying to find the
  1131.  * volume label in the first directory block.
  1132.  */
  1133.  
  1134. int
  1135. IdentifyDisk(name, date)
  1136. char           *name;        /* Should be at least 32 characters */
  1137. struct DateStamp *date;
  1138. {
  1139.     debug(("IdentifyDisk\n"));
  1140.     ReadBootBlock();        /* Also sets default vollabel */
  1141.  
  1142.     if (IDDiskType == ID_DOS_DISK) {
  1143.     byte           *dirblock;
  1144.     struct MsDirEntry *dirent;
  1145.  
  1146.     if (dirblock = ReadSec(Disk.rootdir)) {
  1147.         dirent = (struct MsDirEntry *) dirblock;
  1148.  
  1149.         while ((byte *) dirent < &dirblock[Disk.bps]) {
  1150.         if (dirent->msd_Attributes & ATTR_VOLUMELABEL) {
  1151.             Disk.vollabel.de_Msd = *dirent;
  1152.             Disk.vollabel.de_Sector = Disk.rootdir;
  1153.             Disk.vollabel.de_Offset = (byte *) dirent - dirblock;
  1154.             OtherEndianMsd(&Disk.vollabel.de_Msd);
  1155.             Disk.vollabel.de_Msd.msd_Cluster = 0;    /* to be sure */
  1156.             break;
  1157.         }
  1158.         dirent++;
  1159.         }
  1160.         strncpy(name, Disk.vollabel.de_Msd.msd_Name, L_8 + L_3);
  1161.         ZapSpaces(name, name + L_8 + L_3);
  1162.         ToDateStamp(date, msd_CreationDate(Disk.vollabel.de_Msd),
  1163.                   msd_CreationTime(Disk.vollabel.de_Msd));
  1164.         debug(("Disk is called '%s'\n", name));
  1165.  
  1166.         FreeSec(dirblock);
  1167.  
  1168.         return 0;
  1169.     }
  1170.     }
  1171.     return 1;
  1172. }
  1173.  
  1174. /*
  1175.  * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so
  1176.  * we use a workaround. The correct thing to do is shown but not used.
  1177.  */
  1178.  
  1179. void
  1180. TDRemChangeInt()
  1181. {
  1182.     if (DiskChangeReq) {
  1183.     struct IOExtTD *req = DiskIOReq;
  1184.  
  1185. #if 0                /* V1.2 and V1.3 have a broken
  1186.                  * TD_REMCHANGEINT */
  1187.     req->iotd_Req.io_Command = TD_REMCHANGEINT;
  1188.     req->iotd_Req.io_Data = (void *) DiskChangeReq;
  1189.     MyDoIO(&req->iotd_Req);
  1190.     WaitIO(&DiskChangeReq->iotd_Req);
  1191. #else
  1192.     Forbid();
  1193.     Remove(&DiskChangeReq->io_Message.mn_Node);
  1194.     Permit();
  1195. #endif
  1196.     DeleteExtIO((struct IORequest *)DiskChangeReq);
  1197.     DiskChangeReq = NULL;
  1198.     }
  1199. }
  1200.  
  1201. /*
  1202.  * Set the disk change SoftInt. Return nonzero on failure.
  1203.  */
  1204.  
  1205. int
  1206. TDAddChangeInt(interrupt)
  1207. struct Interrupt *interrupt;
  1208. {
  1209.     struct IOExtTD *req = DiskIOReq;
  1210.  
  1211.     if (DiskChangeReq) {
  1212.     TDRemChangeInt();
  1213.     }
  1214.     DiskChangeReq = (void *)CreateExtIO(DiskReplyPort,
  1215.                      (long) sizeof (*DiskChangeReq));
  1216.     if (DiskChangeReq) {
  1217.     /* Clone IO request part */
  1218.     DiskChangeReq->io_Device = req->iotd_Req.io_Device;
  1219.     DiskChangeReq->io_Unit = req->iotd_Req.io_Unit;
  1220.     DiskChangeReq->io_Command = TD_ADDCHANGEINT;
  1221.     DiskChangeReq->io_Data = (void *) interrupt;
  1222.     debug(("Sending TD_ADDCHANGEINT\n"));
  1223.     SendIO((struct IORequest *)DiskChangeReq);
  1224.     debug(("Done sending TD_ADDCHANGEINT\n"));
  1225.  
  1226.     return 0;
  1227.     }
  1228.     return 1;
  1229. }
  1230.  
  1231. /*
  1232.  * Get the current disk change number. Necessary for ETD_ commands. Makes
  1233.  * absolutely sure nobody can change the disk without us noticing it.
  1234.  */
  1235.  
  1236. int
  1237. TDChangeNum()
  1238. {
  1239.     struct IOExtTD *req = DiskIOReq;
  1240.  
  1241.     req->iotd_Req.io_Command = TD_CHANGENUM;
  1242.     MyDoIO(&req->iotd_Req);
  1243.     req->iotd_Count = req->iotd_Req.io_Actual;
  1244.  
  1245.     return req->iotd_Req.io_Actual;
  1246. }
  1247.  
  1248. /*
  1249.  * Get the current write protection state.
  1250.  *
  1251.  * Zero means writable, one means write protected, minus one means
  1252.  * no disk in drive.
  1253.  */
  1254.  
  1255. int
  1256. TDProtStatus()
  1257. {
  1258.     struct IOExtTD *req = DiskIOReq;
  1259.  
  1260.     req->iotd_Req.io_Command = TD_PROTSTATUS;
  1261.     MyDoIO(&req->iotd_Req);
  1262.  
  1263.     if (req->iotd_Req.io_Error)
  1264.     return -1;
  1265.  
  1266.     return req->iotd_Req.io_Actual != 0;
  1267. }
  1268.  
  1269. /*
  1270.  * Switch the drive motor off. Return previous state.
  1271.  */
  1272.  
  1273. int
  1274. TDMotorOff()
  1275. {
  1276.     struct IOExtTD *req = DiskIOReq;
  1277.  
  1278.     req->iotd_Req.io_Command = TD_MOTOR;
  1279.     req->iotd_Req.io_Length = 0;
  1280.     MyDoIO(&req->iotd_Req);
  1281.  
  1282.     return req->iotd_Req.io_Actual;
  1283. }
  1284.  
  1285. /*
  1286.  * Clear all internal messydisk buffers.
  1287.  */
  1288.  
  1289. int
  1290. TDClear()
  1291. {
  1292.     struct IOExtTD *req = DiskIOReq;
  1293.  
  1294.     req->iotd_Req.io_Command = CMD_CLEAR;
  1295.  
  1296.     return MyDoIO(&req->iotd_Req);
  1297. }
  1298.  
  1299. #if ! READONLY
  1300. /*
  1301.  * Write out all internal messydisk buffers to the disk.
  1302.  */
  1303.  
  1304. int
  1305. TDUpdate()
  1306. {
  1307.     struct IOExtTD *req = DiskIOReq;
  1308.  
  1309.     req->iotd_Req.io_Command = ETD_UPDATE;
  1310.  
  1311.     return MyDoIO(&req->iotd_Req);
  1312. }
  1313. #endif
  1314.  
  1315. int
  1316. MyDoIO(ioreq)
  1317. struct IOStdReq *ioreq;
  1318. {
  1319.     ioreq->io_Flags |= IOF_QUICK;    /* Preserve IOMDF_40TRACKS */
  1320.     BeginIO((struct IORequest *)ioreq);
  1321.     return WaitIO((struct IORequest *)ioreq);
  1322. }
  1323.